let Vue

// 循环对象
function forEach(obj, callback) {
  Object.keys(obj).forEach(key => {
    callback(key, obj[key])
  })
}

function installModule(store, rootState, path, rawModule) {
  console.log('path', path)
  console.log('rawModule', rawModule)
  /**
   * rawModule: _raw(原模块) 、_children、state
   */

  if (path.length > 0) {
    console.log('这里执行了没')
    // 当前path > 0，说明有子模块
    let parentState = path.slice(0, -1).reduce((root, current) => {
      return root[current]
    }, rootState)
    Vue.set(parentState, path[path.length - 1], rawModule.state)
  }

  let getters = rawModule._raw.getters
  if (getters) {
    forEach(getters, (getterName, value) => {
      Object.defineProperty(store.getters, getterName, {
        get: () => {
          return value(rawModule.state)
        }
      })
    })
  }

  let mutations = rawModule._raw.mutations
  if (mutations) {
    forEach(mutations, (mutationName, value) => {
      let arr =
        store.mutations[mutationName] || (store.mutations[mutationName] = [])
      arr.push(payload => {
        value(rawModule.state, payload)
      })
    })
  }

  let actions = rawModule._raw.actions
  if (actions) {
    forEach(actions, (actionName, value) => {
      let arr = store.actions[actionName] || (store.actions[actionName] = [])
      arr.push(payload => {
        value(store, payload)
      })
    })
  }

  // 进行递归安装模块
  forEach(rawModule._children, (moduleName, rawModule) => {
    installModule(store, rootState, path.concat(moduleName), rawModule)
  })
}

class ModuleCollection {
  constructor(options) {
    // 深度遍历所有的子模块
    this.register([], options)
  }

  register(path, rootModule) {
    let rawModule = {
      _raw: rootModule,
      _children: {},
      state: rootModule.state
    }
    if (!this.root) {
      this.root = rawModule
    } else {
      // ['a', 'b', 'c', 'd'].slice(0, -1) => 返回值是  ['a', 'b', 'c'] (相当于把最后一个元素给截取掉了)
      // ['a'].slice(0, -1) => 返回值是 [] (是个空数组)
      // 不停的找到要定义的模块，将这个模块定义到他的父亲上
      let parendteModule = path.slice(0, -1).reduce((root, current) => {
        return root._children[current]
      }, this.root)
      parendteModule._children[path[path.length - 1]] = rawModule
    }

    if (rootModule.modules) {
      // 如果传入的配置信息中有modules，需要进行格式化
      forEach(rootModule.modules, (moduleName, module) => {
        // 递归注册
        // a模块进行注册: [a], a模块的定义
        // b模块进行注册: [b], b模块的定义
        // c模块进行注册: [b, c], c模块的定义
        this.register(path.concat(moduleName), module)
      })
    }
  }
}

class Store {
  constructor(options) {
    // 此处是为了实现响应式数据（借用Vue来实现的）
    this.vm = new Vue({
      data: {
        state: options.state
      }
    })
    this.getters = {}
    this.mutations = {}
    this.actions = {}

    // 1.需要将用户传入的数据进行格式化
    this.modules = new ModuleCollection(options)
    console.log('this.modules', this.modules)
    // 2.递归安装模块 参数： store/rootState/path/跟模块
    installModule(this, this.state, [], this.modules.root)

    // 数据结构
    /* let root = {
      _raw: rootModule,
      state: rootModule.state,
      _children: {
        a: {
          _raw: aModule,
          stae: aModule.state,
          _children: {}
        },
        b: {
          _raw: bModule,
          stae: bModule.state,
          _children: {}
        },
        c: {
          _raw: cModule,
          stae: cModule.state,
          _children: {}
        }
      }
    } */
  }

  commit = (mutationName, payload) => {
    // this.mutations[mutationName](payload) // 发布
    this.mutations[mutationName].forEach(fn => fn(payload)) // 遍历执行 发布
  }
  dispatch(actionName, payload) {
    // this.actions[actionName](payload)
    this.actions[actionName].forEach(fn => fn(payload))
  }
  // es6中 类的实例访问器
  get state() {
    return this.vm.state
  }
}
const install = _Vue => {
  // 传递的是外部的Vueg构造函数
  // 不能直接放在Vue构造函数的原型上
  // 正确的做法：只从当前的根实开始 所有的根实例的子组件才有$store
  Vue = _Vue
  Vue.mixin({
    // 混入
    // 组件的创建过程是先父后子
    beforeCreate() {
      if (this.$options.store) {
        // 根实例
        this.$store = this.$options.store
      } else {
        this.$store = this.$parent && this.$parent.$store
      }
    }
  })
}

export default {
  Store,
  install
}
